#include "stdafx.h"
#include "../include/ut_debug.h"
#include <time.h>
#include <iostream>

namespace utility
{
namespace log{

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN  // We only need minimal includes
#include <windows.h>
#define snprintf _snprintf    // see comment in strutil.cc
#elif defined(HAVE_PTHREAD)
#include <pthread.h>
#else
#error "No suitable threading library available."
#endif

LogMessage& LogMessage::operator<<(const std::string& value) {
    message_ += value;
    return *this;
}

LogMessage& LogMessage::operator<<(const char* value) {
    message_ += value;
    return *this;
}

// Since this is just for logging, we don't care if the current locale changes
// the results -- in fact, we probably prefer that.  So we use snprintf()
// instead of Simple*toa().
#undef DECLARE_STREAM_OPERATOR
#define DECLARE_STREAM_OPERATOR(TYPE, FORMAT)                       \
LogMessage& LogMessage::operator<<(TYPE value) {                \
/* 128 bytes should be big enough for any of the primitive */   \
/* values which we print with this, but well use snprintf() */  \
/* anyway to be extra safe. */                                  \
char buffer[128];                                               \
snprintf(buffer, sizeof(buffer), FORMAT, value);                \
/* Guard against broken MSVC snprintf(). */                     \
buffer[sizeof(buffer)-1] = '\0';                                \
message_ += buffer;                                             \
return *this;                                                   \
}

DECLARE_STREAM_OPERATOR(char         , "%c" )
DECLARE_STREAM_OPERATOR(int          , "%d" )
DECLARE_STREAM_OPERATOR(uint         , "%u" )
DECLARE_STREAM_OPERATOR(long         , "%ld")
DECLARE_STREAM_OPERATOR(unsigned long, "%lu")
DECLARE_STREAM_OPERATOR(double       , "%g" )
#undef DECLARE_STREAM_OPERATOR

LogMessage::LogMessage(LogLevel level, const char* filename, int line)
    : level_(level), filename_(filename), line_(line) {}
LogMessage::~LogMessage() {}

void LogMessage::Finish() {
    if (CDebug::Instance()->IsDebugType(CDebug::e_DefaultType) && CDebug::Instance()->IsDebugLevel(level_, CDebug::e_DefaultType))
    {
        static const char * log_names[] = {
            "CRITICAL"     ,
            "IMPORTANT"    ,
            "INFO"         ,
            "USERCRITICAL" ,
            "USERIMPORTANT",
            "USERINFO"     ,
            "DEBUGINFO"    ,
            "DEFAULT"     
        };
        ostringstream oss;
        oss << '['<<log_names[level_] <<']'<< message_;
        if (CDebug::Instance()->OutputFileName() || CDebug::Instance()->OutputLineNumber())
        {
            oss << '(';
            if (CDebug::Instance()->OutputFileName())
                oss << filename_ << ',';
            if (CDebug::Instance()->OutputLineNumber())
                oss << line_;
            oss << ')';
        }
        oss << '\n';
        cdbg.write(oss.str().c_str(), (int)oss.str().length());
    }
}

void LogFinisher::operator=(LogMessage& other) {
    other.Finish();
}
/************************************************************************/
/* CDebugSink                                                           */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: public  CDebugSink::CDebugSink
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSink::CDebugSink () : m_bEnableHeader(false)
{
    
}

std::string CDebugSink::StandardHeader ()
{
    // Fetch the current time
    time_t now = time(0);
    return  std::string(ctime(&now)).substr(11, 9);
}

/************************************************************************/
/* CDebugSinkNull                                                       */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: private  CDebugSinkNull::CDebugSinkNull
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSinkNull::CDebugSinkNull ()
{
}


/************************************************************************/
/* CDebugSinkConsole                                                    */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: protected  CDebugSinkConsole::CDebugSinkConsole
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSinkConsole::CDebugSinkConsole() { }

//////////////////////////////////////////////////////////////////////////
// Method: virtual protected  CDebugSinkConsole::~CDebugSinkConsole
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSinkConsole::~CDebugSinkConsole()
{
    Flush();
}

//////////////////////////////////////////////////////////////////////////
// Method: public  CDebugSinkConsole::Write
// Remark: 
// @param const std::string& str :
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkConsole::Write(const std::string& str)
{
    m_strBuffer += str;
    Flush();
}

//////////////////////////////////////////////////////////////////////////
// Method: public  CDebugSinkConsole::Write
// Remark: 
// @param int c :
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkConsole::Write(int c)
{
    m_strBuffer += (char)c;
    if (c == '\n')
    {
        Flush();
    }
}

//////////////////////////////////////////////////////////////////////////
// Method: virtual protected  CDebugSinkConsole::Display
// Remark: 
// @param const std::string& str :
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkConsole::Display(const std::string& str)
{
    std::cout << str;
}

//////////////////////////////////////////////////////////////////////////
// Method: public  CDebugSinkConsole::Flush
// Remark: 
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkConsole::Flush()
{
    if (m_strBuffer.empty())
    {
        return;
    }

    if (m_bEnableHeader)
    {
        m_strBuffer = Header() + m_strBuffer;
    }

    // Add a trailing newline if we don't have one
    // (we need this when we shut down)
    if (m_strBuffer[m_strBuffer.length()-1] != '\n')
    {
        m_strBuffer += '\n';
    }

    Display(m_strBuffer);
    m_strBuffer.clear();
}

/************************************************************************/
/* CDebugSinkConsole                                                    */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: private  CDebugSinkFile::CDebugSinkFile
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSinkFile::CDebugSinkFile() : 
    m_nLastDate(0)
{
    m_strFileTail = ".log";
}

//////////////////////////////////////////////////////////////////////////
// Method: virtual private  CDebugSinkFile::~CDebugSinkFile
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebugSinkFile::~CDebugSinkFile()
{
    Flush();
}

//////////////////////////////////////////////////////////////////////////
/// Method: public  GSUT::CDebugSinkFile::SetFileDir
/// Remark: 
/// @param const std::string& dir :
/// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkFile::SetFileDir(const std::string& dir)
{
    m_strDir = dir;
}

void CDebugSinkFile::SetFileHead(const std::string& head)
{
    m_strFileHead = head;
}

//////////////////////////////////////////////////////////////////////////
/// Method: public  GSUT::CDebugSinkFile::SetFileTail
/// Remark: 
/// @param const std::string& tail :
/// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkFile::SetFileTail(const std::string& tail)
{
    m_strFileTail = tail;
}

//////////////////////////////////////////////////////////////////////////
/// Method: private  GSUT::CDebugSinkFile::UpdateLogFile
/// Remark: 
/// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkFile::UpdateLogFile()
{
    //time_t now = time(0);
    std::string strFilePath;
    time_t t = time(NULL);    
    tm ptm;
    uint temp = (uint)(t / 86400);

    if (m_strFileName.empty())
    {
        char szToday[64] = {0};
#ifdef WIN32
        localtime_s(&ptm, &t);
        ::_snprintf(szToday, 64, "%d_%.2d_%.2d", ptm.tm_year+1900, ptm.tm_mon+1, ptm.tm_mday);
#else
        localtime_r(&t, &ptm);
        snprintf(szToday, 64, "%d_%.2d_%.2d", ptm.tm_year+1900, ptm.tm_mon+1, ptm.tm_mday);
#endif
        
        

        m_nLastDate = temp;// get the date
        m_strFileName = szToday;
        strFilePath = m_strDir + "/" + m_strFileHead + m_strFileName + m_strFileTail;
        m_fsOutput.open(strFilePath.c_str(), std::ios_base::app);
    }
    else
    {
        if (m_nLastDate != temp)
        {
            char szToday[64] = {0};
#ifdef WIN32
            localtime_s(&ptm, &t);
            ::_snprintf(szToday, 64, "%d_%.2d_%.2d", ptm.tm_year+1900, ptm.tm_mon+1, ptm.tm_mday);
#else
            localtime_r(&t, &ptm);
            snprintf(szToday, 64, "%d_%.2d_%.2d", ptm.tm_year+1900, ptm.tm_mon+1, ptm.tm_mday);
#endif
            m_nLastDate = temp;
            m_strFileName = szToday;
            strFilePath = m_strDir + "/" + m_strFileHead + m_strFileName + m_strFileTail;
            m_fsOutput.close();
            m_fsOutput.open(strFilePath.c_str(), std::ios_base::app);
        }
    }
}

//////////////////////////////////////////////////////////////////////////
// Method: virtual private  CDebugSinkFile::Display
// Remark: 
// @param const std::string& str :
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkFile::Display(const std::string& str)
{
    
    UpdateLogFile();
    m_fsOutput << str;
    m_fsOutput.flush();
}


/************************************************************************/
/* CDebugSinkWindows                                                    */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: virtual private  CDebugSinkWindows::Display
// Remark: 
// @param const std::string& str :
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebugSinkWindows::Display(const std::string& str)
{
#ifdef WIN32
    OutputDebugString(str.c_str());
#endif
}


/************************************************************************/
/* CDebug                                                               */
/************************************************************************/

//////////////////////////////////////////////////////////////////////////
// Method: public  CDebug::Reset
// Remark: 
// @return void :
//////////////////////////////////////////////////////////////////////////
void CDebug::Reset ()
{
    m_bsDebugType.reset();
    m_vecDebugLevel.clear();
    m_vecDebugLevel.resize(e_MaxDebugType /*DEFAULT*/);
}

//////////////////////////////////////////////////////////////////////////
// Method: private  CDebug::CDebug
// Remark: 
// @return :
//////////////////////////////////////////////////////////////////////////
CDebug::CDebug ()
{
    m_bsDebugType.reset();
    TurnOnDebugType(e_DefaultType);
    m_bOutputFileName = true;
    m_bOutputLineNumber = true;
    m_vecDebugLevel.clear();
    m_vecDebugLevel.resize(32 /*DEFAULT*/);
}

}
}

std::ostream utility::log::cdbg(CDebug::Instance()->GetDebugStream());
